package org.convallaria.infrastruct.ce.aspect;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.convallaria.infrastruct.ce.annotation.CacheEvictAll;
import org.convallaria.infrastruct.ce.annotation.CacheRefresh;
import org.convallaria.infrastruct.ce.annotation.CacheWarmup;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.concurrent.CompletableFuture;

/**
 * 缓存AOP切面
 * 处理自定义缓存注解
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
@Aspect
@Component
@Order(1)
@RequiredArgsConstructor
public class CacheAspect {

    private final CacheManager cacheManager;
    private final ExpressionParser expressionParser = new SpelExpressionParser();

    /**
     * 处理缓存刷新注解
     */
    @Around("@annotation(cacheRefresh)")
    public Object handleCacheRefresh(ProceedingJoinPoint joinPoint, CacheRefresh cacheRefresh) throws Throwable {
        
        // 检查条件
        if (!evaluateCondition(cacheRefresh.condition(), joinPoint)) {
            return joinPoint.proceed();
        }
        
        if (cacheRefresh.async()) {
            // 异步刷新
            CompletableFuture.runAsync(() -> {
                try {
                    refreshCaches(cacheRefresh.value(), joinPoint);
                } catch (Exception e) {
                    log.error("异步刷新缓存失败", e);
                }
            });
            return joinPoint.proceed();
        } else {
            // 同步刷新
            if (cacheRefresh.delay() > 0) {
                Thread.sleep(cacheRefresh.delay());
            }
            refreshCaches(cacheRefresh.value(), joinPoint);
            return joinPoint.proceed();
        }
    }

    /**
     * 处理缓存预热注解
     */
    @Around("@annotation(cacheWarmup)")
    public Object handleCacheWarmup(ProceedingJoinPoint joinPoint, CacheWarmup cacheWarmup) throws Throwable {
        
        // 检查条件
        if (!evaluateCondition(cacheWarmup.condition(), joinPoint)) {
            return joinPoint.proceed();
        }
        
        if (cacheWarmup.async()) {
            // 异步预热
            CompletableFuture.runAsync(() -> {
                try {
                    if (cacheWarmup.delay() > 0) {
                        Thread.sleep(cacheWarmup.delay());
                    }
                    warmupCaches(cacheWarmup.value(), joinPoint);
                } catch (Exception e) {
                    log.error("异步预热缓存失败", e);
                }
            });
            return joinPoint.proceed();
        } else {
            // 同步预热
            if (cacheWarmup.delay() > 0) {
                Thread.sleep(cacheWarmup.delay());
            }
            warmupCaches(cacheWarmup.value(), joinPoint);
            return joinPoint.proceed();
        }
    }

    /**
     * 处理批量清除缓存注解
     */
    @Around("@annotation(cacheEvictAll)")
    public Object handleCacheEvictAll(ProceedingJoinPoint joinPoint, CacheEvictAll cacheEvictAll) throws Throwable {
        if (cacheEvictAll.beforeInvocation()) {
            evictAllCaches(cacheEvictAll.value());
        }
        
        Object result = joinPoint.proceed();
        
        if (!cacheEvictAll.beforeInvocation()) {
            evictAllCaches(cacheEvictAll.value());
        }
        
        return result;
    }

    /**
     * 刷新指定缓存
     */
    private void refreshCaches(String[] cacheNames, ProceedingJoinPoint joinPoint) {
        for (String cacheName : cacheNames) {
            try {
                Cache cache = cacheManager.getCache(cacheName);
                if (cache != null) {
                    cache.clear();
                    log.info("刷新缓存: {}", cacheName);
                }
            } catch (Exception e) {
                log.error("刷新缓存失败: {}", cacheName, e);
            }
        }
    }

    /**
     * 预热指定缓存
     */
    private void warmupCaches(String[] cacheNames, ProceedingJoinPoint joinPoint) {
        for (String cacheName : cacheNames) {
            try {
                Cache cache = cacheManager.getCache(cacheName);
                if (cache == null) {
                    log.warn("缓存不存在，跳过预热: {}", cacheName);
                    continue;
                }
                
                // 预热策略：根据缓存名称执行不同的预热逻辑
                if (cacheName.contains("user")) {
                    warmupUserCache(cache);
                } else if (cacheName.contains("config")) {
                    warmupConfigCache(cache);
                } else {
                    // 通用预热：清空缓存，让下次访问时重新加载
                    cache.clear();
                    log.info("清空缓存以触发预热: {}", cacheName);
                }
                
                log.info("预热缓存完成: {}", cacheName);
            } catch (Exception e) {
                log.error("预热缓存失败: {}", cacheName, e);
            }
        }
    }
    
    /**
     * 预热用户缓存
     */
    private void warmupUserCache(Cache cache) {
        try {
            // 预热常用用户数据
            for (long i = 1; i <= 10; i++) {
                String key = "user:" + i;
                if (cache.get(key) == null) {
                    // 这里可以调用用户服务获取数据并放入缓存
                    // 实际实现中应该注入用户服务
                    log.debug("预热用户缓存: {}", key);
                }
            }
        } catch (Exception e) {
            log.warn("预热用户缓存失败", e);
        }
    }
    
    /**
     * 预热配置缓存
     */
    private void warmupConfigCache(Cache cache) {
        try {
            // 预热系统配置数据
            String[] configKeys = {"system.config", "app.config", "cache.config"};
            for (String key : configKeys) {
                if (cache.get(key) == null) {
                    // 这里可以调用配置服务获取数据并放入缓存
                    log.debug("预热配置缓存: {}", key);
                }
            }
        } catch (Exception e) {
            log.warn("预热配置缓存失败", e);
        }
    }

    /**
     * 清除所有匹配的缓存
     */
    private void evictAllCaches(String[] patterns) {
        if (patterns.length == 0) {
            // 清除所有缓存
            cacheManager.getCacheNames().forEach(cacheName -> {
                Cache cache = cacheManager.getCache(cacheName);
                if (cache != null) {
                    cache.clear();
                }
            });
        } else {
            // 根据模式清除缓存
            for (String pattern : patterns) {
                cacheManager.getCacheNames().stream()
                        .filter(cacheName -> matchesPattern(cacheName, pattern))
                        .forEach(cacheName -> {
                            Cache cache = cacheManager.getCache(cacheName);
                            if (cache != null) {
                                cache.clear();
                                log.info("清除缓存: {}", cacheName);
                            }
                        });
            }
        }
    }

    /**
     * 检查缓存名称是否匹配模式
     */
    private boolean matchesPattern(String cacheName, String pattern) {
        if (pattern.contains("*")) {
            return cacheName.matches(pattern.replace("*", ".*"));
        }
        return cacheName.equals(pattern);
    }

    /**
     * 评估SpEL条件表达式
     */
    private boolean evaluateCondition(String condition, ProceedingJoinPoint joinPoint) {
        if (!StringUtils.hasText(condition)) {
            return true;
        }
        
        try {
            Expression expression = expressionParser.parseExpression(condition);
            EvaluationContext context = new StandardEvaluationContext();
            
            // 设置方法参数
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String[] paramNames = signature.getParameterNames();
            Object[] args = joinPoint.getArgs();
            
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
            
            return Boolean.TRUE.equals(expression.getValue(context, Boolean.class));
        } catch (Exception e) {
            log.warn("评估条件表达式失败: {}", condition, e);
            return true;
        }
    }
}
